﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;

using SS4U.Library.ConfigurationClasses;

namespace SS4U.Library
{
    public class Searcher
    {

        public SearchResultCollection Search(string searchPhrase, int? startNodeId, string searchDefinitionName)
        {
            var returnValue = new SearchResultCollection();

            var startNode = startNodeId.HasValue ? new umbraco.presentation.nodeFactory.Node(startNodeId.Value) : new umbraco.presentation.nodeFactory.Node(-1);
// ReSharper disable ConditionIsAlwaysTrueOrFalse
            if (startNode == null)
// ReSharper restore ConditionIsAlwaysTrueOrFalse
            {
                umbraco.BusinessLogic.Log.Add(umbraco.BusinessLogic.LogTypes.Error, 0, "Error in SS4U.library.Searcher.Search: no StartNode");
                return returnValue;
            }

            var searchDefinition = string.IsNullOrEmpty(searchDefinitionName) ? Configuration.DefaultSearchDefinition : Configuration.GetSearchDefinition(searchDefinitionName);

            if (searchDefinition == null)
            {
                umbraco.BusinessLogic.Log.Add(umbraco.BusinessLogic.LogTypes.Error, 0, "Error in SS4U.library.Searcher.Search: no default search definition");
                return returnValue;
            }
            DoSearch(returnValue, startNode, searchPhrase.ToLower(), searchDefinition);
            return returnValue;
        }

        private static SearchResult CreateSearchResult(umbraco.presentation.nodeFactory.Node node, DocumentType dt)
        {
            return new SearchResult
                       {
                           Id = node.Id,
                           Text = node.Name,
                           LinkUrl = umbraco.library.NiceUrl(node.Id),
                           Category = dt.Category,
                           CreateDate = node.CreateDate
                       };
        }

        private static void TestNode(ICollection<SearchResult> searchResults, umbraco.presentation.nodeFactory.Node item, DocumentType dt, string searchPhrase)
        {
            if (dt.TestNodeName && item.Name.ToLower().IndexOf(searchPhrase) != -1)
            {
                searchResults.Add(CreateSearchResult(item, dt));
                return;
            }
            foreach (var dtprop in dt.Properties)
            {
                var umbracoProperty = item.GetProperty(dtprop.Alias);
                if (umbracoProperty != null && !umbracoProperty.Value.IsNullOrTrimmedEmpty())
                {
                    switch (dtprop.ComparisonType)
                    {
                        case ComparisonType.String:
                            switch (dtprop.Comparison)
                            {
                                case Comparison.Equal:
                                    if (umbracoProperty.Value.ToLower() == searchPhrase)
                                    {
                                        searchResults.Add(CreateSearchResult(item, dt));
                                        return;
                                    }
                                    break;
                                case Comparison.GreaterThan:
                                    break;
                                case Comparison.LowerThan:
                                    break;
                                case Comparison.StartsWith:
                                    if (umbracoProperty.Value.ToLower().StartsWith(searchPhrase,
                                                                                   StringComparison.CurrentCulture))
                                    {
                                        searchResults.Add(CreateSearchResult(item, dt));
                                        return;
                                    }
                                    break;
                                case Comparison.Contains:
                                    if (umbracoProperty.Value.ToLower().IndexOf(searchPhrase) != -1)
                                    {
                                        searchResults.Add(CreateSearchResult(item, dt));
                                        return;
                                    }
                                    break;
                                default:
                                    break;
                            }

                            break;
                        case ComparisonType.Integer:
                            int intVal;
                            int intSearchPhrase;
                            if (int.TryParse(umbracoProperty.Value, out intVal) &&
                                int.TryParse(searchPhrase, out intSearchPhrase))
                            {
                                switch (dtprop.Comparison)
                                {
                                    case Comparison.Equal:
                                        if (intVal == intSearchPhrase)
                                        {
                                            searchResults.Add(CreateSearchResult(item, dt));
                                            return;
                                        }
                                        break;
                                    case Comparison.GreaterThan:
                                        if (intSearchPhrase > intVal)
                                        {
                                            searchResults.Add(CreateSearchResult(item, dt));
                                            return;
                                        }
                                        break;
                                    case Comparison.LowerThan:
                                        if (intSearchPhrase > intVal)
                                        {
                                            searchResults.Add(CreateSearchResult(item, dt));
                                            return;
                                        }
                                        break;
                                    case Comparison.Contains:
                                        break;
                                    case Comparison.StartsWith:
                                        break;
                                    default:
                                        break;
                                }
                            }
                            break;
                    }
                }
            }
        }

        private void DoSearch(ICollection<SearchResult> searchResults, umbraco.presentation.nodeFactory.Node parentNode, string searchPhrase, SearchDefinition searchDefinition)
        {
            //umbraco.BusinessLogic.Log.Add(umbraco.BusinessLogic.LogTypes.Debug, parentNode.Id, "Performing SS4U Search on parentNode '" + parentNode.Name + "'");
            foreach (umbraco.presentation.nodeFactory.Node item in parentNode.Children)
            {
                var item2 = item;
                var dt = (from x in searchDefinition.DocumentTypes where x.Alias == item2.NodeTypeAlias select x).FirstOrDefault();

                if (dt != null)
                    TestNode(searchResults, item, dt, searchPhrase);

                if (searchDefinition.IterationType == IterationType.IterateAll || (dt != null && dt.IncludeChildNodes))
                    DoSearch(searchResults, item, searchPhrase, searchDefinition);
            }
        }

        #region XPathNodeIterators
// ReSharper disable UnusedMember.Global
        public static XPathNodeIterator SearchToXml(string searchPhrase, int startNodeId, string searchDefinitionName)
// ReSharper restore UnusedMember.Global
        {
            var s = new Searcher();
            int? i = null;
            if (startNodeId > 0)
                i = startNodeId;
            return ConvertSearchResultsToXml(s.Search(searchPhrase, i, searchDefinitionName));
        }

        private static XPathNodeIterator ConvertSearchResultsToXml(SearchResultCollection results)
        {
            var ser = new XmlSerializer(typeof(SearchResultCollection));
            var xml = new XmlDocument();
            
            using (var ms = new System.IO.MemoryStream())
            {
                ser.Serialize(ms, results);
                ms.Position = 0;
                xml.Load(ms);
                ms.Close();
            }

            var xp = xml.CreateNavigator();
// ReSharper disable PossibleNullReferenceException
            return xp.Select("/SearchResults");
// ReSharper restore PossibleNullReferenceException
        }
        #endregion

    }
}

